package org.thattoolkit.srvctl;
import java.io.File;
import java.net.URL;
import java.util.Iterator;
import java.util.Stack;
import java.util.regex.Pattern;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.Writer;

import org.apache.commons.configuration.AbstractHierarchicalFileConfiguration;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.ConfigurationKey;
import org.apache.commons.configuration.HierarchicalConfiguration;
import org.apache.commons.configuration.HierarchicalConfiguration.Node;
import org.apache.commons.configuration.HierarchicalConfiguration.NodeVisitor;
import org.apache.commons.configuration.tree.ConfigurationNode;
import org.apache.commons.configuration.tree.ConfigurationNodeVisitor;
import org.apache.commons.configuration.tree.DefaultConfigurationNode;

import com.sun.org.apache.xerces.internal.impl.xs.identity.Selector.Matcher;

class ApachePrintVisitor implements ConfigurationNodeVisitor {
	private PrintWriter out;
	private int indent;
	private int delta;
	
	private final int default_indent = 0;
	private final int default_delta = 4;
	
	public int getIndent() {
		return indent;
	}

	public void setIndent(int indent) {
		this.indent = indent;
	}

	public int getDelta() {
		return delta;
	}

	public void setDelta(int delta) {
		this.delta = delta;
	}

	public ApachePrintVisitor(Writer _out) {
		out = new PrintWriter(_out);
		indent = default_indent;
		delta = default_delta;
	}

	private void doIndent() {
		for(int i = 0; i < indent; i++)
			for(int j = 0; j < delta; j++)
				out.print(' ');
	}
	
	public void visitBeforeChildren(ConfigurationNode node) {
		boolean hasChildren = node.getChildrenCount() != 0;
		doIndent();
		if(hasChildren) 
			out.print('<');
		out.print(node.getName());
		out.print(' ');
		out.print(node.getValue().toString());
		if(hasChildren)
			out.print('>');
		out.println();
		if(hasChildren)
			indent++;
	}

	public void visitAfterChildren(ConfigurationNode node) {
		boolean hasChildren = node.getChildrenCount() != 0;
		if(hasChildren) {
			doIndent();
			out.print("</");
			out.print(node.getName());
			out.print('>');
			out.println();
			indent--;
		}
	}

	public boolean terminate() {
		// No reason to stop visiting
		return false;
	}
}

/**
 * @author ulrb
 *
 */
public class ApacheHttpdConfiguration extends
		AbstractHierarchicalFileConfiguration {
	
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	/**
	 * 
	 */
	public ApacheHttpdConfiguration() {
		super();
	}
	
	/**
	 * @param filename
	 * @throws ConfigurationException
	 */
	public ApacheHttpdConfiguration(String filename) throws ConfigurationException {
		super(filename);
	}
	
	/**
	 * @param file
	 * @throws ConfigurationException
	 */
	public ApacheHttpdConfiguration(File file) throws ConfigurationException {
		super(file);
	}
	
	/**
	 * @param url
	 * @throws ConfigurationException
	 */
	public ApacheHttpdConfiguration(URL url) throws ConfigurationException {
		super(url);
	}
	
	/**
	 * Creates a node with the given name. This simply calls createNode(name) and ignores
	 * the isSect parameter. This method is meant to be overwritten by derived classes
	 * @param name The name of the section
	 * @param isSect Is this a section?
	 * @return the created Node element
	 */
	public Node createNode(String name, boolean isSect) {
		return createNode(name);
	}
	
	/**
	 * Calls parseSection(line, false)
	 * @param line
	 * @return
	 */
	protected Node parseSection(String line) {
		return parseSection(line, false);
	}
	
	/**
	 * Generates a ConfigurationNode by parsing one single line of text, using the first
	 * whitespace separated token as the name and the remainder of the line as the value
	 * of the ConfigurationNode.
	 * @param line
	 * @param isSect
	 * @return
	 * @throws ConfigurationException 
	 */
	protected Node parseSection(String line, boolean isSect) throws ConfigurationException {
		String parts[] = line.split("\\s+",1);
		Node node = createNode(parts[0], isSect);
		if(node == null)
			throw new ConfigurationException(line+": Could not create entry");
		node.setValue(parts[1]);
		return node;
	}

	protected int lineNo = 0;

	/**
	 * @param arg0
	 * @throws ConfigurationException
	 */
	@Override
	public void load(Reader arg0) throws ConfigurationException {
		lineNo = 0;
		try {
			BufferedReader bReader = new BufferedReader(arg0);
			Node section = getRoot();
			Stack<String> sectionStack = new Stack<String>();
			DefaultConfigurationNode comments = new DefaultConfigurationNode();

			if(section == null) {
				section = createNode("default");
				setRoot(section);
			}
			while(true) {
				String line = bReader.readLine();
				if(line == null)
					break;
				lineNo++;
				line = line.trim();
				if(line.isEmpty())
					continue;
				if(line.charAt(0) == '#') {
					Node node = createNode("#");
					node.setValue(line.substring(1).trim());
					section.addChild(node);
					continue;
				}
				if(line.charAt(0) == '<') {
					if(line.charAt(1) == '/') {
						if(sectionStack.isEmpty())
							throw new ConfigurationException(getFileName()+"["+lineNo+"]: Section mismatch: "+line);
						String sect = line.substring(2, line.length()-1);
						String prevSect = sectionStack.pop();
						if(prevSect != sect)
							throw new ConfigurationException(getFileName()+"["+lineNo+"]: Section mismatch: "+prevSect+"/"+sect);
						section = section.getParent();
					}
					else {
						String sect = line.substring(1, line.length()-1);
						Node newSection = parseSection(sect, true);
						sectionStack.push(newSection.getName());
						section.addChild(newSection);
						section = newSection;
					}
				}
				else {
					section.addChild(parseSection(line));
				}
			}
		}
		catch(IOException e) {
			throw new ConfigurationException(getFileName()+"["+lineNo+"]: Cannot load Configuration", e);
		}
	}

	/**
	 * @param arg0
	 * @throws ConfigurationException
	 */
	@Override
	public void save(Writer arg0) throws ConfigurationException {
		Node root = getRoot();
		if(root != null) 
			root.visit(new ApachePrintVisitor(arg0));
	}
}
